package map;

import ui.*;
import mob.*;

public class Map {

  private int mapWidth, mapHeight;
  Screen screen;
  boolean [][] seen;
  Creature [][] creature;
  Creature player;

String [] tmp;
String [] tmp0 = {
"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT",
"TT&..&TT&&T&.&..T&..&.T&&TT.&.&.T&.&.&T&.T",
"T.&......T&.&..&.......T......T&.&T.&&.TTT",
"TT.....&..&&.T..&........&....&###%###.&.T",
"T..T.....T&...&&T&.....T.......#,,,,,#.T&T",
"T&&&&&..&.&..&T.&T&&...........#,,,,,#&.TT",
"T&.T&&&TT&&.T&&&.&.&T..........,,,,,,#T&TT",
"T&&&&.T&&.&&&&T&.&&.........&..#,,,,,#&T&T",
"T&T.&&&.......&&T.&.&...&......%,,,,,#..TT",
"T....&&&.-~~~.&&.&&&.T&........#######&&.T",
"~--..&&.-~~~~~~.~......&..T........&.T&&TT",
"~~~-~~~---...~~~~~~~~.......&...&....&..&T",
"T~~~~~........--~~~~~~-..&.......&....T&.T",
"T&&.T&.T&&......-~~~~~~~-..&....&...&T.&&T",
"T.&T&T&&.......~~~~~~~~~~~---..&.....&&&.T",
"T&&T.T&....&....-~~~~~~~~~~----......&..TT",
"TT.&&&.&........-~~~~~~~~~-~~~..T.....T..T",
"T&.&TT........--~~~~~~~~~~~~~~~~~~&.....&T",
"TT&&.&&T&&..-~~~---~~~~~~~~~~~~~~~~~--...~",
"T&.&..&TT---~~~~~---.....-~~~~~~~~~~~---~~",
"T&T&T.&&---~~~~---.......--~~~~~~~~~---~~~",
"TT&~~~~~~---~~~--...T.....-~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~-~-.........---~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~.........----~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~...---......--~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~..---~~-......--~~~~~~~~~~~~",
"~~~~~~~~~~~~~.~~~~~~~......--~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~~~~~~~...~~~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
};
String [] tmp1 = {
"##########################################",
"#...........#####################...######",
"#...................................######",
"#...........#########.###########...######",
"#...........#########.############.#######",
"#########.#########.........######.#######",
"#########.#########.........######.#######",
"#########.#########.........######.#######",
"#########.#########.........######.#######",
"#######......######.........######.#######",
"#######......############.########.#######",
"#######......############.########.#######",
"#######.........###.......########.#######",
"#######.........###.#.#####..........#####",
"#######.............#.#...#.########.#####",
"#######.........#####.#.#.#.########.#####",
"#######.........###...#.#.#.###.........##",
"#########.#########.###.###.###.........##",
"#########.#########.........###.........##",
"#########.#####################.........##",
"#########.##########################.#####",
"#######.....########################.#####",
"#######.....########################.#####",
"#######.....##########......########.#####",
"########.#############......########.#####",
"########.##########............#####.#####",
"#......#.............................#####",
"#......#.##########............###########",
"#........#############......##############",
"#......###############......##############",
"#......###################################",
"##########################################"
};

  public Map(Screen s) {

    screen = s;
    mapWidth = screen.mapWidth;
    mapHeight = screen.mapHeight;

    tmp = tmp1;

    creature = new Creature[mapWidth][mapHeight];
    seen = new boolean[mapWidth][mapHeight];
    
    for(int i = 0; i < mapWidth; i++) {
      for(int j = 0; j < mapHeight; j++) {
        creature[i][j] = null;
        seen[i][j] = false;
      }
    }

  }

  public void display() {
    for(int i = 0; i < mapWidth; i++) {
      for(int j = 0; j < mapHeight; j++) {
        int [] grey = {40, 40, 40};
        int [] black = {0, 0, 0};
        boolean vis = isVisible(i, j, 10);
        if(vis) seen[i][j] = true;
        /* creatures take top priority */
        if(creature[i][j] != null) {
          creature[i][j].setDisplay(screen.mapChar[i][j]);
          continue;
        }
        if(tmp[j].charAt(i) == 'T') {
          int [] color = {0, 100, 0};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar((char) 9827, color);
        } else if(tmp[j].charAt(i) == '#') {
          int [] color = {200, 180, 50};
          int [] bgcolor = {120, 75, 30};
          if(!vis && seen[i][j]) color = black;
          if(!vis && seen[i][j]) bgcolor = grey;
          if(!vis && !seen[i][j]) color = black;
          if(!vis && !seen[i][j]) bgcolor = black;
          screen.mapChar[i][j].setChar((char) '#', color);
          screen.mapChar[i][j].setChar((char) 0x2584, color);
          screen.mapChar[i][j].setBackground(bgcolor);
        } else if(tmp[j].charAt(i) == '%') {
          int [] color = {200, 180, 50};
          int [] bgcolor = {120, 75, 30};
          if(!vis && seen[i][j]) color = black;
          if(!vis && seen[i][j]) bgcolor = grey;
          if(!vis && !seen[i][j]) color = black;
          if(!vis && !seen[i][j]) bgcolor = black;
          screen.mapChar[i][j].setChar((char) '#', color);
          screen.mapChar[i][j].setChar((char) 0x2588, color);
          screen.mapChar[i][j].setBackground(bgcolor);
        } else if(tmp[j].charAt(i) == '&') {
          int [] color = {50, 250, 50};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar('&', color);
        } else if(tmp[j].charAt(i) == '.') {
          int [] color = {90, 255, 90};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar('.', color);
        } else if(tmp[j].charAt(i) == ',') {
          int [] color = {120, 75, 30};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar('.', color);
        } else if(tmp[j].charAt(i) == '~') {
          int [] color = {0, 0, 200};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar((char) 8776, color);
        } else if(tmp[j].charAt(i) == '-') {
          int [] color = {80, 80, 255};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar((char) 8776, color);
        } else if(tmp[j].charAt(i) == '@') {
          int [] color = {200, 50, 50};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar('@', color);
          screen.mapChar[i][j].setChar((char) 65312, color);
        } else if(tmp[j].charAt(i) == 'g') {
          int [] color = {100, 100, 255};
          if(!vis && seen[i][j]) color = grey;
          if(!vis && !seen[i][j]) color = black;
          screen.mapChar[i][j].setChar('g', color);
        }
      }
    }
  }

  public void placePlayer(Creature p, int x, int y) {
    p.setLocation(x, y);
    creature[x][y] = p;
    player = p;
  }

  public void moveCreature(Creature c, int cx, int cy) {
    int sx = c.mapX;
    int sy = c.mapY;
    int dx = sx + cx;
    int dy = sy + cy;
    if(dx >= mapWidth || dx < 0) return;
    if(dy >= mapHeight || dy < 0) return;
    if(tmp[dy].charAt(dx) == 'T' || tmp[dy].charAt(dx) == '#' || tmp[dy].charAt(dx) == '~' || tmp[dy].charAt(dx) == '%') return;
    c.setLocation(dx, dy);
    creature[sx][sy] = null;
    creature[dx][dy] = c;
  }

  boolean isVisible(int x, int y, int d) {

    int px, py;
    int cx, cy; /* current coordinates */
    double slope;
    boolean vertical, positive;
    double r = (double) d;
    double b = 1.0;
    double sx, sy, dx, dy;  /* points inside the square */

    px = player.mapX;
    py = player.mapY;

    if(Math.sqrt(Math.pow(px - x, 2) + Math.pow(py - y, 2)) > d) return(false);

    if(Math.abs(y - py) > Math.abs(x - px)) {
      vertical = true;
      slope = (double) (x - px) / (double) (y - py);
      if((y - py) > 0) positive = true;
      else positive = false;
    } else {
      vertical = false;
      slope = (double) (y - py) / (double) (x - px);
      if((x - px) > 0) positive = true;
      else positive = false;
    }
    
    cx = px;
    cy = py;
    sx = 9.0;
    sy = 9.0;

    while(x != cx || y != cy) {

      /* if we are moving primarily along the y axis */
      if(vertical) {
        if(positive) dy = 18.0;
        else dy = 0.0;
        dx = sx + (slope * (dy - sy));
        if(dx < 0.0) {
          double dr = Math.abs(0.0 - sx) / Math.abs(sx - dx);
          dx = 0.0;
          dy = sy + ((dy - sy) * dr);
        } else if (dx > 18.0) {
          double dr = Math.abs(18.0 - sx) / Math.abs(sx - dx);
          dx = 18.0;
          dy = sy + ((dy - sy) * dr);
        }
      }

      /* if we are moving primarily along the x axis */
      else {
        if(positive) dx = 18.0;
        else dx = 0.0;
        dy = sy + (slope * (dx - sx));
        if(dy < 0.0) {
          double dr = Math.abs(0.0 - sy) / Math.abs(sy - dy);
          dy = 0.0;
          dx = sx + ((dx - sx) * dr);
        } else if (dy > 18.0) {
          double dr = Math.abs(18.0 - sy) / Math.abs(sy - dy);
          dy = 18.0;
          dx = sx + ((dx - sx) * dr);
        }
      }

      /* decrease the remaining vision as per terrain */
      double ddxx = Math.pow(Math.abs(dx) - Math.abs(sx), 2.0);
      double ddyy = Math.pow(Math.abs(dy) - Math.abs(sy), 2.0);
      double dd = Math.sqrt(ddxx + ddyy) / 18.0;
      double z = 1.0;
      if(tmp[cy].charAt(cx) == '#') {
        z = 6.0;
        b -= dd;
      }
      else if(tmp[cy].charAt(cx) == '%') {
        z = 6.0;
        b -= dd;
      }
      else if(tmp[cy].charAt(cx) == 'T') z = 6.0;
      else if(tmp[cy].charAt(cx) == '&') z = 6.0;
      r -= (dd * z);

      /* bail if the remaining vision length does not reach the destination */
      if(r < 0 || b <= 0) {
        return(false);
      }

      /* define all the cases where a boarder value transitions */
      boolean ypos = false; /* y = 18 -> 0 */
      if(vertical && positive) ypos = true;
      else if(!vertical && positive && slope > 0) ypos = true;
      else if(!vertical && !positive && slope < 0) ypos = true;
      boolean yneg = false; /* y = 0 -> 18 */
      if(vertical && !positive) yneg = true;
      else if(!vertical && positive && slope < 0) yneg = true;
      else if(!vertical && !positive && slope > 0) yneg = true;
      boolean xpos = false; /* x = 18 -> 0 */
      if(!vertical && positive) xpos = true;
      else if(vertical && positive && slope > 0) xpos = true;
      else if(vertical && !positive && slope < 0) xpos = true;
      boolean xneg = false; /* y = 0 -> 18 */
      if(!vertical && !positive) xneg = true;
      else if(vertical && positive && slope < 0) xneg = true;
      else if(vertical && !positive && slope > 0) xneg = true;

      /* switch x boarder values */
      if(dx == 18.0 && xpos) {
        sx = 0.0;
        cx++;
      } else if(dx == 0.0 && xneg) {
        sx = 18.0;
        cx--;
      } else {
        sx = dx;
      }

      /* switch y boarder values */
      if(dy == 18.0 && ypos) {
        sy = 0.0;
        cy++;
      } else if(dy == 0.0 && yneg) {
        sy = 18.0;
        cy--;
      } else {
        sy = dy;
      }

    }

    return(true);

  }

}
